#include <iostream>
#include <glm/gtx/norm.hpp>
#include "glbReader.hpp"

bool testGLBReader()
{
    GLBReader reader;
    reader.ReadGLBFile("data/glb_demo.glb");
    
    uint32_t sceneCnt = reader.GetSceneCnt();
    if( sceneCnt != 1)
    {
        std::cout << "TestGLBReader.Test_0: Expected scene size: 1, but got: " << sceneCnt << std::endl;
        return false;
    }

    const nlohmann::json& nodeList = reader.GetNodeIndicies( 0 );
    if( nodeList.size() != 5 )
    {
        std::cout << "TestGLBReader.Test_1: Expected root node size: 5, but got: " << nodeList.size() << std::endl;
        return false;
    }

    bool res = true;
    uint32_t expedNodes[5] = { 5, 6, 7, 8, 9 };
    for( uint32_t i = 0; res && i < nodeList.size(); ++i )
    { res = expedNodes[i] == nodeList[i]; }
    if( !res )
    {
        std::cout << "TestGLBReader.Test_2: node indicies does not match." << std::endl;
        return false; 
    }
    
    const nlohmann::json& nodeInfo = reader.GetNodeInfo(5);
    const nlohmann::json& children = reader.GetNodeChildren(nodeInfo);
    if( children.size() != 3 )
    {
        std::cout << "TestGLBReader.Test_3: Expected node children size: 3, but got: " << children.size() << std::endl;
        return false;
    }

    uint32_t expChildNodes[3] = { 0, 1, 4 };
    for( uint32_t i = 0; res && i < children.size(); ++i )
    { res = expChildNodes[i] == children[i]; }
    if( !res )
    {
        std::cout << "TestGLBReader.Test_4: node children indicies does not match." << std::endl;
        return false; 
    }

    const nlohmann::json& meshInfo_5 = reader.GetMeshInfo(5);
    uint32_t submeshCnt = reader.GetSubmeshCnt( meshInfo_5 );
    if( submeshCnt != 1 )
    {
        std::cout << "TestGLBReader.Test_5: Expected sub mesh cnt: 1, but got: " << submeshCnt << std::endl;
        return false; 
    }

    GLBBufferView bvs[4];
    bvs[0].attribute = GLB_ATTR_POSITION;
    bvs[1].attribute = GLB_ATTR_NORMAL;
    bvs[2].attribute = GLB_ATTR_TEXCOORD_0;
    bvs[3].attribute = GLB_ATTR_INDICIES;
    reader.FetchNodeMeshProp( meshInfo_5, bvs, 4 );

    const uint32_t expSizes[] = { 288, 288, 192, 72 };
    const uint32_t expType[] = { 3, 3, 2, 1 };
    const uint32_t expCount[] = { 24, 24, 24, 36 };
    for( uint32_t i = 0; res && i < 4; ++i )
    { res = bvs[i].length == expSizes[i] && bvs[i].type == expType[i] && bvs[i].count == expCount[i]; }
    if( !res )
    {
        std::cout << "TestGLBReader.Test_6: failed to check buffer size and type." << std::endl;
        return false; 
    }

    const nlohmann::json& node_0 = reader.GetNodeInfo(0);
    uint32_t childrenCnt_0 = reader.GetNodeChildrenCnt( node_0 );
    if( childrenCnt_0 != 0 )
    {
        std::cout << "TestGLBReader.Test_7: Expected child cnt: 0, but got: " << childrenCnt_0 << std::endl;
        return false; 
    }

    const nlohmann::json& node_6 = reader.GetNodeInfo(6);
    NodeDetail detail;
    reader.GetNodeDetail( node_6, detail );
    if( detail.meshIndex != 6 )
    {
        std::cout << "TestGLBReader.Test_8: Expected mesh index: 6, but got: " << detail.meshIndex << std::endl;
        return false; 
    }

    glm::vec3 pos = detail.locTrans[3];
    const float expdist = glm::distance2( pos, glm::vec3(0.0f, 6.03475f, 3.1403f) );
    if( glm::abs(expdist) > 0.00001f )
    {
        std::cout << "TestGLBReader.Test_9: Position data check failed. " << std::endl;
        return false; 
    }

    float* parray = (float*) bvs[0].ptr;
    glm::vec3 p0 = glm::vec3(parray[0], parray[1], parray[2] );
    if( glm::distance2( p0, glm::vec3(1.0f, 1.0f, -1.0f) ) > 0.0000001f )
    {
        std::cout << "TestGLBReader.Test_10: Fetched bad binary data. " << std::endl;
        return false; 
    }

    const nlohmann::json& node_9 = reader.GetNodeInfo(9);
    reader.GetNodeDetail( node_9, detail );
    if( detail.meshIndex != UINT32_MAX )
    {
        std::cout << "TestGLBReader.Test_11: Expected no mesh info for the node. " << std::endl;
        return false; 
    }
    glm::vec3 pos_9 = detail.locTrans[3];
    const float expdist_9 = glm::distance2( pos_9, glm::vec3(0.0f, 1.26899f, -3.41591f) );
    if( glm::abs(expdist_9) > 0.00001f )
    {
        std::cout << "TestGLBReader.Test_12: Position data check failed. " << std::endl;
        return false; 
    }
        
    std::cout << "TestGLBReader TEST OK.";
    return true;
}

int main()
{
    bool res = true;
    bool(*funcs[])() = {
        testGLBReader
    };
    size_t fncCnt = sizeof(funcs) / sizeof(void*);

    clock_t te = 0;
    for(size_t i = 0; i < fncCnt && res; i++)
    {
        clock_t t = clock();
        res = funcs[i]();
        t = clock() - t;
        te += t;
        std::cout << ".........." << t << " ticks. (" << ((float)t)/CLOCKS_PER_SEC << " secs)" << std::endl;
    }
    if(res){
        std::cout << "======= All Test OK =======" << std::endl;
        std::cout << "Test took total: " << te << " ticks. (" << ((float)te)/CLOCKS_PER_SEC << " secs)" << std::endl;
    }    
    return 0;
}